package joc.internal.transformer;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import javassist.CtBehavior;
import javassist.CtClass;
import javassist.CtMethod;
import joc.customize.Contract;
import joc.customize.ForMethodEvaluateAtEndOfMethodWithReturnValueAndInherit;
import joc.internal.compiler.CastExp;
import joc.internal.compiler.NestedExp;

public class PostConditionMethodTransformer extends DeclaredMethodTransformer {

	public PostConditionMethodTransformer(Transformer transformer) {
		super(transformer);
	}

	@Override
	public void transform(CtClass clazz, CtMethod method) throws Exception {
		getLogger().debug("method " + method.getLongName());
		for (CtBehavior consultedBehavior : getConsultedBehaviors(method)) {
			getLogger().info("consulted behavior " + consultedBehavior.getLongName());
			for (Class<? extends Annotation> contractAnnotation : Transformer.getContractMap().keySet()) {
				Annotation annotation = (Annotation) consultedBehavior.getAnnotation(contractAnnotation);
				if (annotation != null) {
					getLogger().info(
							"method " + method.getLongName() + " has contract "
									+ Transformer.getContractMap().get(contractAnnotation).getClass().getName());
					applyContract(method, Transformer.getContractMap().get(contractAnnotation), contractAnnotation,
							annotation);
				}
			}
		}
	}

	private void applyContract(CtMethod method, Class<? extends Contract<?>> contract,
			Class<? extends Annotation> annotationClass, Annotation annotation) throws Exception {
		for (Method contractMethod : getContractMethodsWithAnnotation(contract,
				ForMethodEvaluateAtEndOfMethodWithReturnValueAndInherit.class)) {
			NestedExp returnValue = handlePrimitiveExp(contractMethod.getParameterTypes()[1], NestedExp.RETURN_VALUE);
			getContractCallString(method, contractMethod, contract, annotationClass, annotation, true,
					new CastExp(contractMethod.getParameterTypes()[1], returnValue)).insertAfter(method);
			getLogger().info("inserted " + contract.getClass().getName() + " in method " + method.getLongName());
		}
	}

}
